{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "chapter-intro",
   "metadata": {},
   "source": [
    "# 第 2 章：LangGraph 框架概览\n",
    "\n",
    "> 本笔记文件需要与《LangGraph实战》的第 2 章的内容配套使用。\n",
    "\n",
    "在构建复杂的智能体应用时，我们经常面临多轮对话、状态管理、工作流程控制等挑战。传统的线性处理方式往往难以胜任这些复杂场景。\n",
    "\n",
    "LangGraph 是基于 LangChain 生态的智能体框架，采用图架构设计，将智能体的工作流程抽象为由节点和边构成的有向循环图。这种架构为构建状态化、可循环的智能体系统提供了强大的支持。\n",
    "\n",
    "本章将通过手工构建 ReAct 智能体的完整示例，帮助您理解 LangGraph 的核心概念和实际应用。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "section-2-1",
   "metadata": {},
   "source": [
    "## 2.1 LangGraph 简介\n",
    "\n",
    "LangGraph 的核心创新在于其采用了图架构来设计智能体的工作流程。这种架构与传统的线性流程或树状结构截然不同，LangGraph 将智能体执行任务的过程，抽象为一个由节点（Node）和边（Edge）构成的**有向循环图（Directed Cyclic Graph）**。\n",
    "\n",
    "### 🔧 核心组件\n",
    "\n",
    "#### 节点（Node）类型\n",
    "- **LLM 调用节点**: 负责与大型语言模型进行交互，执行推理任务\n",
    "- **工具调用节点**: 允许智能体调用外部工具或 API，扩展能力边界\n",
    "- **自定义函数节点**: 封装业务逻辑或数据处理逻辑\n",
    "- **子图节点**: 提高代码模块化和可维护性\n",
    "\n",
    "#### 边（Edge）类型\n",
    "- **普通边**: 定义节点间的直接顺序执行关系\n",
    "- **条件边**: 根据状态动态决定工作流程走向\n",
    "- **入口点**: 定义工作流程的起始位置\n",
    "- **条件入口点**: 根据初始状态动态选择起始节点\n",
    "\n",
    "#### 状态（State）\n",
    "状态是 LangGraph 的核心概念，扮演着多重角色：\n",
    "- **上下文信息存储**: 记录对话历史、任务进度等\n",
    "- **节点间数据传递**: 作为共享的数据容器\n",
    "- **状态持久化**: 支持记忆能力和容错恢复\n",
    "- **多智能体共享**: 实现智能体间协作\n",
    "\n",
    "现在让我们通过实际代码来理解这些概念的应用。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "environment-setup",
   "metadata": {},
   "source": [
    "### 🚀 环境准备\n",
    "\n",
    "首先加载必要的环境变量配置："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "cell-0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from dotenv import load_dotenv\n",
    "\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "section-2-2",
   "metadata": {},
   "source": [
    "## 2.2 LangGraph 与 LangChain 的关系\n",
    "\n",
    "在深入实践之前，让我们理解 LangGraph 与 LangChain 的关系。\n",
    "\n",
    "### 核心架构差异\n",
    "\n",
    "| 特性 | LangChain (LCEL) | LangGraph |\n",
    "|------|------------------|----------|\n",
    "| **核心架构** | 链式，有向无环图 (DAG) | 图架构，有向循环图 (DCG) |\n",
    "| **设计理念** | 简化 LLM 应用开发，线性流程 | 构建复杂、状态化的智能体系统 |\n",
    "| **状态管理** | 相对简单，短期记忆 | 强大的状态管理和持久化机制 |\n",
    "| **工作流程** | 线性或分支流程，相对固定 | 循环流程，支持迭代和动态分支 |\n",
    "| **适用场景** | 简单应用，快速原型开发 | 复杂智能体，多智能体系统 |\n",
    "\n",
    "### 协同使用策略\n",
    "\n",
    "最佳实践是将 LangChain 作为 LangGraph 的“组件库”：\n",
    "- 利用 LangChain 的模型、提示、工具等组件\n",
    "- 在 LangGraph 中组织和编排这些组件\n",
    "- 构建更复杂的工作流程和智能体系统\n",
    "\n",
    "现在让我们实际构建一个展示这些概念的 ReAct 智能体。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "section-2-3",
   "metadata": {},
   "source": [
    "## 2.3 基于 LangGraph 实现 ReAct 模式\n",
    "\n",
    "为了帮助大家深入理解 LangGraph 框架的图构建过程，并掌握如何从零开始搭建智能体工作流程，我们将通过手工构建图的方式来实现一个工具调用型的 ReAct 模式智能体。\n",
    "\n",
    "我们将使用 OpenAI 兼容的模型作为示例，构建一个能够使用搜索工具查询天气信息的智能体。与预制 API 不同，这次我们从最基础的 LangGraph 组件开始，一步步构建 ReAct 智能体的工作流程图。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-2-1",
   "metadata": {},
   "source": [
    "##### 示例 2-1：定义工具和工具节点\n",
    "\n",
    "首先导入必要的 LangGraph 和 LangChain 组件，并定义智能体可以使用的工具。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "cell-1",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI  # 导入 ChatOpenAI，用于使用 OpenAI 的模型\n",
    "from langchain_core.tools import tool  # 导入 tool 装饰器，用于定义工具\n",
    "from langgraph.graph import END, StateGraph, MessagesState  # 导入 LangGraph 图构建核心组件：END, StateGraph, MessagesState\n",
    "from langgraph.prebuilt import ToolNode  # 导入 ToolNode，用于封装工具节点\n",
    "\n",
    "# 定义工具：search，用于模拟网页搜索，查询天气信息 (与之前的示例相同)\n",
    "@tool\n",
    "def search(query: str):\n",
    "    \"\"\"Call to surf the web.\"\"\"\n",
    "    # 这是一个占位符工具，实际应用中需要替换为真正的搜索功能\n",
    "    if \"sf\" in query.lower() or \"san francisco\" in query.lower():\n",
    "        return \"It's 60 degrees and foggy.\"\n",
    "    return \"It's 90 degrees and sunny.\"\n",
    "\n",
    "tools = [search] # 将 search 工具放入工具列表\n",
    "tool_node = ToolNode(tools) # 创建 ToolNode 实例，将工具列表封装成 LangGraph 节点"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "explanation-2-1",
   "metadata": {},
   "source": [
    "**💡 核心概念解析**：\n",
    "\n",
    "在这段代码中，我们定义了 `search` 工具，并使用 `ToolNode` 将工具列表封装成一个 LangGraph 节点。\n",
    "\n",
    "- `@tool` 装饰器：将普通 Python 函数转换为 LangChain 工具\n",
    "- `ToolNode`：LangGraph 预置的节点类型，专门用于执行工具调用\n",
    "- 工具的 docstring 作为工具描述提供给 LLM，帮助模型理解何时使用工具"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-2-2",
   "metadata": {},
   "source": [
    "##### 示例 2-2：初始化模型并绑定工具\n",
    "\n",
    "初始化语言模型，并将其配置为能够调用工具。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "cell-2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 初始化语言模型，使用 Qwen/Qwen3-8B 模型，并绑定工具\n",
    "model = ChatOpenAI(model=\"Qwen/Qwen3-8B\", temperature=0).bind_tools(tools)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "explanation-2-2",
   "metadata": {},
   "source": [
    "**💡 关键概念**：\n",
    "\n",
    "`bind_tools(tools)` 方法至关重要。它将我们定义的工具列表绑定到 ChatOpenAI 模型上，使得模型知道它有哪些工具可以使用，以及如何调用这些工具。只有绑定了工具的模型，才能在后续的推理过程中生成工具调用指令。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-2-3",
   "metadata": {},
   "source": [
    "##### 示例 2-3：创建状态结构体\n",
    "\n",
    "创建 StateGraph 实例，指定状态类型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "cell-3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义状态类型为 MessageState，用于处理消息列表\n",
    "workflow = StateGraph(MessagesState)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "explanation-2-3",
   "metadata": {},
   "source": [
    "**💡 StateGraph 核心概念**：\n",
    "\n",
    "- `StateGraph`：LangGraph 中用于构建和管理图的核心类\n",
    "- `MessagesState`：LangGraph 预置的状态类型，专门用于处理消息列表，非常适合构建对话型智能体\n",
    "- 状态作为\"记忆库\"，用于存储和传递信息，在不同节点间共享数据"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "examples-2-4-2-5",
   "metadata": {},
   "source": [
    "##### 示例 2-4 & 2-5：添加节点\n",
    "\n",
    "向图中添加两个核心节点：\n",
    "1. \"agent\" 节点：负责调用语言模型进行推理\n",
    "2. \"tools\" 节点：负责执行工具调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "cell-4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<langgraph.graph.state.StateGraph at 0x120c90980>"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 定义 agent 节点的执行函数：call_model\n",
    "def call_model(state):\n",
    "    messages = state['messages'] # 从状态中获取消息列表\n",
    "    response = model.invoke(messages) # 调用语言模型 model 进行推理，输入为消息列表\n",
    "    return {\"messages\": [response]} # 将模型响应消息封装成字典返回，键为 \"messages\"，值为包含响应消息的列表\n",
    "\n",
    "# 将 call_model 函数添加到图中，并命名为 \"agent\" 节点\n",
    "workflow.add_node(\"agent\", call_model)\n",
    "\n",
    "# 将之前创建的 tool_node 实例添加到图中，并命名为 \"tools\" 节点\n",
    "workflow.add_node(\"tools\", tool_node)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "explanation-2-4-2-5",
   "metadata": {},
   "source": [
    "**💡 节点设计原理**：\n",
    "\n",
    "- `call_model` 函数：节点的执行函数，接收状态作为输入，返回状态更新\n",
    "- 返回格式：必须是字典形式 `{\"messages\": [response]}`，用于更新 LangGraph 状态\n",
    "- 双节点架构：Agent 负责推理决策，Tools 负责行动执行，形成 ReAct 循环的基础"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-2-6",
   "metadata": {},
   "source": [
    "##### 示例 2-6：设置图的入口点\n",
    "\n",
    "定义工作流程的起始节点。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "cell-5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<langgraph.graph.state.StateGraph at 0x120c90980>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 设置图的入口点为 \"agent\" 节点，表示工作流程从 agent 节点开始执行\n",
    "workflow.set_entry_point(\"agent\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-2-7",
   "metadata": {},
   "source": [
    "##### 示例 2-7：定义条件判断函数\n",
    "\n",
    "定义条件判断逻辑，这是 ReAct 模式的核心：根据模型输出决定下一步行动。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "cell-6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义条件判断函数：should_continue，决定下一步执行哪个节点\n",
    "def should_continue(state):\n",
    "    messages = state['messages'] # 从状态中获取消息列表\n",
    "    last_message = messages[-1] # 获取最后一条消息，即 agent 节点的输出消息\n",
    "    # 如果 agent 节点的输出消息中包含工具调用指令，则流向 \"tools\" 节点\n",
    "    if last_message.tool_calls:\n",
    "        return \"tools\"\n",
    "    # 否则，工作流程结束，流向 END\n",
    "    return END"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "explanation-2-7",
   "metadata": {},
   "source": [
    "**💡 条件路由机制**：\n",
    "\n",
    "`should_continue` 函数实现了 ReAct 的核心决策逻辑：\n",
    "- 检查最后一条消息是否包含 `tool_calls`\n",
    "- 如果有工具调用，返回 \"tools\"（继续行动）\n",
    "- 否则返回 `END`（推理完成，结束流程）\n",
    "- `END` 是 LangGraph 预定义的特殊值，表示工作流程终点"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-2-8",
   "metadata": {},
   "source": [
    "##### 示例 2-8：添加条件边\n",
    "\n",
    "添加从 agent 节点出发的条件边，根据条件函数的返回值决定流向。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "cell-7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<langgraph.graph.state.StateGraph at 0x120c90980>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 添加条件边：从 \"agent\" 节点出发，根据 should_continue 函数的返回值，决定流向 \"tools\" 节点或 END\n",
    "workflow.add_conditional_edges(\n",
    "    \"agent\", # 起始节点为 \"agent\" 节点\n",
    "    should_continue # 条件判断函数为 should_continue\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-2-9",
   "metadata": {},
   "source": [
    "##### 示例 2-9：添加普通边\n",
    "\n",
    "添加固定的返回路径，从 tools 节点总是返回到 agent 节点。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "cell-8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<langgraph.graph.state.StateGraph at 0x120c90980>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 添加普通边：从 \"tools\" 节点到 \"agent\" 节点，表示工具调用完成后，总是返回 agent 节点继续推理\n",
    "workflow.add_edge(\"tools\", 'agent')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "explanation-2-8-2-9",
   "metadata": {},
   "source": [
    "**💡 边的类型和 ReAct 循环**：\n",
    "\n",
    "- **条件边**：`add_conditional_edges()` 实现动态路由，根据运行时状态选择路径\n",
    "- **普通边**：`add_edge()` 创建固定连接，工具执行后总是返回 agent\n",
    "- **ReAct 循环**：这种设计形成了\"推理 → 行动 → 推理 → ...\"的循环模式\n",
    "- **有向循环图**：与传统 DAG 不同，支持循环结构，实现迭代推理"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-2-10",
   "metadata": {},
   "source": [
    "##### 示例 2-10：编译 LangGraph 图\n",
    "\n",
    "将构建好的图编译为可执行的 Runnable 对象。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "cell-9",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 编译 LangGraph 图，得到可执行的 app 对象\n",
    "app = workflow.compile()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "example-2-11",
   "metadata": {},
   "source": [
    "##### 示例 2-11：运行 ReAct 智能体并处理用户查询\n",
    "\n",
    "测试我们构建的 ReAct 智能体，向其发送用户查询。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "cell-10",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "The current weather in San Francisco is 60 degrees Fahrenheit and foggy. You might want to carry a light jacket or consider reduced visibility due to the fog.\n"
     ]
    }
   ],
   "source": [
    "# 运行智能体应用 app，处理用户查询 \"What is the weather in sf\" (旧金山天气)\n",
    "final_state = app.invoke({\"messages\": [{\"role\": \"user\", \"content\": \"what is the weather in sf\"}]})\n",
    "# 打印智能体的最后一条回复消息的内容\n",
    "print(final_state[\"messages\"][-1].content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "execution-analysis",
   "metadata": {},
   "source": [
    "## 🔄 LangGraph 执行流程深度解析\n",
    "\n",
    "让我们深入分析当运行 `app.invoke()` 方法时，LangGraph 内部的执行机制：\n",
    "\n",
    "### 完整执行步骤\n",
    "\n",
    "1. **初始化阶段**：\n",
    "   - LangGraph 将输入消息添加到内部状态\n",
    "   - 状态传递给入口点节点 \"agent\"\n",
    "\n",
    "2. **Agent 节点执行**：\n",
    "   - `call_model` 函数被调用，接收当前状态\n",
    "   - 从状态中提取消息列表，传递给语言模型\n",
    "   - 模型分析问题，识别需要获取天气信息\n",
    "   - 生成包含工具调用指令的 AIMessage\n",
    "\n",
    "3. **条件判断阶段**：\n",
    "   - `should_continue` 函数检查最后一条消息\n",
    "   - 发现 `tool_calls` 存在，返回 \"tools\"\n",
    "   - 工作流程路由到 Tools 节点\n",
    "\n",
    "4. **Tools 节点执行**：\n",
    "   - ToolNode 解析工具调用指令\n",
    "   - 执行 `search(\"weather in sf\")` 函数\n",
    "   - 返回结果：\"It's 60 degrees and foggy.\"\n",
    "   - 工具结果被添加到状态中\n",
    "\n",
    "5. **循环返回 Agent**：\n",
    "   - 根据普通边，流程返回 Agent 节点\n",
    "   - Agent 接收包含工具结果的完整对话历史\n",
    "   - 模型基于工具结果生成最终回答\n",
    "\n",
    "6. **流程结束**：\n",
    "   - 最终回答不包含工具调用\n",
    "   - `should_continue` 返回 END\n",
    "   - 工作流程完成，返回最终状态\n",
    "\n",
    "### ReAct 模式的核心优势\n",
    "\n",
    "这种设计体现了 LangGraph 相比传统线性流程的关键优势：\n",
    "\n",
    "- **动态决策**：根据运行时状态动态选择执行路径\n",
    "- **状态持久化**：完整的对话历史在整个流程中保持可用\n",
    "- **循环支持**：支持多轮推理-行动循环，直到问题解决\n",
    "- **灵活扩展**：易于添加新的工具、节点或修改流程逻辑"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "chapter-conclusion",
   "metadata": {},
   "source": [
    "## 📚 本章总结\n",
    "\n",
    "通过本章的学习，我们深入了解了 LangGraph 框架的核心理念和实际应用。LangGraph 采用有向循环图架构，通过节点、边和状态三个核心组件构建智能体工作流程，相比传统线性流程具有更强的灵活性和表达能力。我们通过手工构建 ReAct 智能体的完整示例，掌握了状态管理、条件路由、循环结构等关键技术，为构建复杂的对话系统、决策型智能体和多智能体协作系统奠定了基础。在下一章中，我们将深入探讨 LangGraph 的图驱动智能体系统，学习更高级的状态管理和复杂流程控制技巧。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
